#include "optionmodel.h"

#include "other/rosterelements.h"
#include "../common/other/unit.h"

OptionModelItem::OptionModelItem(OptionModelItem *_parent) : parentItem(_parent) {
    if(parentItem)
        parentItem->children.append(const_cast<OptionModelItem*>(this));
}

OptionModelItem::~OptionModelItem() {
    qDeleteAll(children);
}

OptionModelItem* OptionModelItem::parent() const {
    return parentItem;
}

int OptionModelItem::childCount() const {
    return children.count();
}

OptionModelItem* OptionModelItem::child(int i) const{
    return children.value(i, NULL);
}

int OptionModelItem::row() const {
    if(parentItem)
        return parentItem->children.indexOf(const_cast<OptionModelItem*>(this));
    return -1;
}

OptionModel::OptionModel(const DualCollection<GameCategory> *_categories, QObject *parent) :
    QAbstractItemModel(parent), categories(_categories), currentUnit(NULL)
{
    Q_ASSERT(categories);
    rootItem = new OptionModelItem(NULL);
}

OptionModel::~OptionModel() {
    delete rootItem;
}

QVariant OptionModel::data(const QModelIndex &index, int role) const {
    if(!index.isValid() || !currentUnit)
        return QVariant();

    OptionModelItem *item = static_cast<OptionModelItem*>(index.internalPointer());

    if(role == Qt::UserRole)
        return item->key;


    if(item->parent() == rootItem) { // Category
        if(role == Qt::DisplayRole) {
            if(item->key.isEmpty())
                return tr("Unclassified");
            else
                return categories->get(item->key).getName();
        }
    } else { // Option
        if(role == Qt::DisplayRole)
            return currentUnit->getBaseUnit().options.get(item->key).getName();
        else if(role == Qt::CheckStateRole)
            return currentUnit->getActivatedOptions().contains(item->key) ? Qt::Checked : Qt::Unchecked;
    }

    return QVariant();
}

Qt::ItemFlags OptionModel::flags(const QModelIndex &index) const {
    if (!index.isValid() || !currentUnit)
        return 0;

    OptionModelItem *item = static_cast<OptionModelItem*>(index.internalPointer());

    if(item->parent() == rootItem)
        return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
    else {
        if(currentUnit->isOptionLocked(item->key))
            return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
        else
            return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable;
    }
}

QVariant OptionModel::headerData(int section, Qt::Orientation orientation,
                    int role) const {
    if(section == 1 && orientation == Qt::Horizontal && role == Qt::DisplayRole)
        return tr("Option");
    return QVariant();
}

int OptionModel::rowCount(const QModelIndex &parent) const {
    OptionModelItem *item = parent.isValid() ? static_cast<OptionModelItem*>(parent.internalPointer()) : rootItem;

    return item->childCount();
}

int OptionModel::columnCount(const QModelIndex &parent) const {
    Q_UNUSED(parent)
    return 1;
}

QModelIndex OptionModel::index(int row, int column,
                  const QModelIndex &parent) const {
    if(!hasIndex(row, column, parent))
        return QModelIndex();

    OptionModelItem *parentItem = parent.isValid() ? static_cast<OptionModelItem*>(parent.internalPointer()) : rootItem;

    return createIndex(row, column, parentItem->child(row));
}

QModelIndex OptionModel::parent(const QModelIndex &index) const {
    if(!index.isValid())
        return QModelIndex();

    OptionModelItem *item = static_cast<OptionModelItem*>(index.internalPointer());
    OptionModelItem *parentItem = item->parent();

    if(parentItem == rootItem) {
        return QModelIndex();
    }

    return createIndex(parentItem->row(), 0, parentItem);
}

bool OptionModel::setData(const QModelIndex &index, const QVariant &value, int role) {
    if(!index.isValid() || role != Qt::CheckStateRole || !currentUnit)
        return false;

    Qt::CheckState targetState = Qt::CheckState(value.toUInt());
    OptionModelItem *item = static_cast<OptionModelItem*>(index.internalPointer());

    emit optionCheckStateToChange(currentUnit, item->key, targetState);
    return true;
}

void OptionModel::forceOptionUpdate(const QString& key) {
    // \TODO: Implement OptionModel::forceOptionUpdate()
}

void OptionModel::setSourceUnit(RosterUnit *unit) {
    if(rootItem->childCount() > 0) {
        beginRemoveRows(QModelIndex(), 0, rootItem->childCount()-1);
        qDeleteAll(rootItem->children);
        rootItem->children.clear();
        endRemoveRows();
    }

    if(unit) {
        QHash<QString, int> categoryIndex;
        Unit baseUnit(unit->getBaseUnit());
        QStringList keys(baseUnit.options.keys());

        for(int i = 0; i<keys.size(); ++i) {
            if(unit->isOptionVisible(keys.at(i))) {
                UnitOption *option = baseUnit.options.getRealElement(keys.at(i));
                int parentIndex;
#ifdef EXTRA_CHECKS
                QString category = categories->contains(option->category) ? option->category : "";
#else
                QString category = option->category;
#endif

                if(categoryIndex.contains(category)) {
                    parentIndex = categoryIndex.value(category);
                } else {
                    parentIndex = rowCount();
                    beginInsertRows(QModelIndex(), parentIndex, parentIndex);
                    categoryIndex.insert(category, parentIndex);
                    OptionModelItem *item = new OptionModelItem(rootItem);
                    item->key = category;
                    endInsertRows();
                }

                int row = rootItem->child(parentIndex)->childCount();
                beginInsertRows(index(parentIndex, 0), row, row);
                OptionModelItem *item = new OptionModelItem(rootItem->child(parentIndex));
                item->key = keys.at(i);
                endInsertRows();
            }
        }
    }

    currentUnit = unit;
}
